# encoding: utf-8
"""
@desc: 计算、创建评测图表
@author: rannuo
@contact: rannuo1010@gmail.com
@time: 2019/4/16 20:56
@file: evaluate_flow.py
"""
import multiprocessing
import os
import time

import matplotlib.pyplot as plt

from alg import common, UserCF, ItemCF, Prob

UserCF_K = 80
ItemCF_K = 10
n_list = [10, 20, 30, 40, 50]  # 推荐列表长度数组
M = 5
mk = 1
seed = 1


def init():
    t0 = time.time()  # 开始时间
    # 读取用户数据
    data = common.get_data(file_path)
    # 划分训练集和测试集
    train, test, train_item_user, test_item_user = common.split_data(data, M, mk, seed)
    print("划分训练集、测试集完成！")
    del data
    t1 = time.time()
    print("加载、初始化数据完成，耗时（秒）：" + str(t1 - t0))

    # 计算相似度矩阵
    user_wuv = UserCF.user_similarity(train)
    t2 = time.time()
    print("UserCF计算用户相似度矩阵完成，耗时（秒）：" + str(t2 - t1))
    item_wuv = ItemCF.item_similarity(train_item_user)
    t3 = time.time()
    print("ItemCF计算物品相似度矩阵完成，耗时（秒）：" + str(t3 - t2))
    G = Prob.gen_graph(train)
    t4 = time.time()
    print("Prob二分图创建完成，耗时（秒）：" + str(t4 - t3))
    return train, test, train_item_user, test_item_user, user_wuv, item_wuv, G


def work_calc_user_cf_evaluation_index(train, test, n, K, user_wuv):
    print('UserCF进程开始执行... PID[%s]' % os.getpid())
    ts = time.time()
    recall, precision, coverage, popularity, accuracy = UserCF.calc_evaluation_index(train, test, n, K, user_wuv)
    te = time.time()
    print("计算UserCF评测指标完成，N=" + str(n) + "，耗时（秒）：" + str(te - ts))
    return recall, precision, coverage, popularity, accuracy


def work_calc_item_cf_evaluation_index(train, test, n, K, user_wuv):
    print('ItemCF进程开始执行... PID[%s]' % os.getpid())
    ts = time.time()
    recall, precision, coverage, popularity, accuracy = ItemCF.calc_evaluation_index(train, test, n, K, user_wuv)
    te = time.time()
    print("计算ItemCF评测指标完成，N=" + str(n) + "，耗时（秒）：" + str(te - ts), end='\n')
    return recall, precision, coverage, popularity, accuracy


def work_calc_prob_evaluation_index(train, test, G, n):
    print('Prob进程开始执行... PID[%s]' % os.getpid())
    ts = time.time()
    recall, precision, coverage, popularity, accuracy = Prob.calc_evaluation_index(train, test, G, n)
    te = time.time()
    print("计算Prob评测指标完成，N=" + str(n) + "，耗时（秒）：" + str(te - ts), end='\n')
    return recall, precision, coverage, popularity, accuracy


def draw_recall_line_chart(x, y_list):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.plot(x, y_list[0], label='User-CF', linewidth=1, color='m', marker='o', markerfacecolor='y', markersize=7)
    plt.plot(x, y_list[1], label='Item-CF', linewidth=1, color='c', marker='o', markerfacecolor='m', markersize=7)
    plt.plot(x, y_list[2], label='Prob', linewidth=1, color='r', marker='o', markerfacecolor='m', markersize=7)
    plt.xlabel('L')
    plt.ylabel('Recall Rate')
    plt.title('召回率折线图')
    plt.legend()
    plt.show()


def draw_precision_line_chart(x, y_list):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.plot(x, y_list[0], label='User-CF', linewidth=1, color='m', marker='s', markerfacecolor='y', markersize=7)
    plt.plot(x, y_list[1], label='Item-CF', linewidth=1, color='c', marker='s', markerfacecolor='m', markersize=7)
    plt.plot(x, y_list[2], label='Prob', linewidth=1, color='r', marker='s', markerfacecolor='m', markersize=7)
    plt.xlabel('L')
    plt.ylabel('Precision Rate')
    plt.title('准确率折线图')
    plt.legend()
    plt.show()


def draw_coverage_line_chart(x, y_list):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.plot(x, y_list[0], label='UserCF', linewidth=1, color='m', marker='s', markerfacecolor='y', markersize=7)
    plt.plot(x, y_list[1], label='ItemCF', linewidth=1, color='c', marker='s', markerfacecolor='m', markersize=7)
    plt.plot(x, y_list[2], label='Prob', linewidth=1, color='r', marker='s', markerfacecolor='m', markersize=7)
    plt.xlabel('L')
    plt.ylabel('Coverage Rate')
    plt.title('覆盖率折线图')
    plt.legend()
    plt.show()


def draw_popularity_line_chart(x, y_list):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.plot(x, y_list[0], label='User-CF', linewidth=1, color='m', marker='^', markerfacecolor='y', markersize=7)
    plt.plot(x, y_list[1], label='Item-CF', linewidth=1, color='c', marker='^', markerfacecolor='m', markersize=7)
    plt.plot(x, y_list[2], label='Prob', linewidth=1, color='r', marker='^', markerfacecolor='m', markersize=7)
    plt.xlabel('L')
    plt.ylabel('Popularity Rate')
    plt.title('平均流行度折线图')
    plt.legend()
    plt.show()


def draw_accuracy_line_chart(x, y_list):
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.plot(x, y_list[0], label='User-CF', linewidth=1, color='m', marker='o', markerfacecolor='y', markersize=7)
    plt.plot(x, y_list[1], label='Item-CF', linewidth=1, color='c', marker='^', markerfacecolor='m', markersize=7)
    plt.plot(x, y_list[2], label='Prob', linewidth=1, color='r', marker='^', markerfacecolor='m', markersize=7)
    plt.xlabel('L')
    plt.ylabel('Accuracy Rate')
    plt.title('算法精度折线图')
    plt.legend()
    plt.show()


def evaluation_flow():
    ts = time.time()  # 开始时间
    res_l = []
    res_2 = []
    res_3 = []
    p = multiprocessing.Pool(3 * len(n_list))
    for n in n_list:
        user_res = p.apply_async(work_calc_user_cf_evaluation_index, args=(u_train, u_test, n, UserCF_K, user_wuv,))
        res_l.append(user_res)
        item_res = p.apply_async(work_calc_item_cf_evaluation_index, args=(i_train, i_test, n, ItemCF_K, item_wuv,))
        res_2.append(item_res)
        prob_res = p.apply_async(work_calc_prob_evaluation_index, args=(u_train, u_test, G, n,))
        res_3.append(prob_res)
    p.close()
    p.join()

    user_cf_recall = []  # User-CF算法召回率
    item_cf_recall = []  # Item-CF算法召回率
    prob_recall = []  # Prob算法召回率

    user_cf_precision = []  # User-CF算法准确率
    item_cf_precision = []  # Item-CF算法准确率
    prob_precision = []  # Prob算法准确率

    user_cf_coverage = []  # User-CF算法覆盖率
    item_cf_coverage = []  # Item-CF算法覆盖率
    prob_coverage = []  # Prob算法覆盖率

    user_cf_popularity = []  # User-CF算法平均流行度
    item_cf_popularity = []  # Item-CF算法平均流行度
    prob_popularity = []  # Prob算法平均流行度

    user_cf_accuracy = []  # User-CF算法精度
    item_cf_accuracy = []  # Item-CF算法精度
    prob_accuracy = []  # Prob算法精度

    for res in res_l:
        ret = res.get()
        user_cf_recall.append(ret[0])
        user_cf_precision.append(ret[1])
        user_cf_coverage.append(ret[2])
        user_cf_popularity.append(ret[3])
        user_cf_accuracy.append(ret[4])

    for res in res_2:
        ret = res.get()
        item_cf_recall.append(ret[0])
        item_cf_precision.append(ret[1])
        item_cf_coverage.append(ret[2])
        item_cf_popularity.append(ret[3])
        item_cf_accuracy.append(ret[4])
    for res in res_3:
        ret = res.get()
        prob_recall.append(ret[0])
        prob_precision.append(ret[1])
        prob_coverage.append(ret[2])
        prob_popularity.append(ret[3])
        prob_accuracy.append(ret[4])
    print("--------------------------------计算完成----------------------------")
    print("评测指标计算完成，总耗时（秒）：" + str(time.time() - ts), end='\n')
    recall_values = [user_cf_recall, item_cf_recall, prob_recall]
    precision_values = [user_cf_precision, item_cf_precision, prob_precision]
    coverage_values = [user_cf_coverage, item_cf_coverage, prob_coverage]
    popularity_values = [user_cf_popularity, item_cf_popularity, prob_popularity]
    accuracy_values = [user_cf_accuracy, item_cf_accuracy, prob_accuracy]
    return recall_values, precision_values, coverage_values, popularity_values, accuracy_values


if __name__ == "__main__":
    ts = time.time()
    file_path = '../data/ratings.txt'
    # file_path = '../data/new/ratings.txt'
    # file_path = '../data/ratings-ml-latest-small.txt'

    # 初始化
    u_train, u_test, i_train, i_test, user_wuv, item_wuv, G = init()
    print("初始化完成，耗时（秒）：" + str(time.time() - ts), end='\n')

    # 计算评测指标
    recall_y, precision_y, coverage_y, popularity_y, accuracy_y = evaluation_flow()

    # 绘制折线图
    draw_recall_line_chart(n_list, recall_y)
    draw_precision_line_chart(n_list, precision_y)
    draw_coverage_line_chart(n_list, coverage_y)
    draw_popularity_line_chart(n_list, popularity_y)
    draw_accuracy_line_chart(n_list, accuracy_y)
    print("处理完成，总耗时（秒）：" + str(time.time() - ts), end='\n')
